﻿using System.Collections.Generic;
using System.Linq;
using Microsoft.TeamFoundation.VersionControl.Client;
using TfsMerge.BO;
using System.ComponentModel;
using System.Windows.Forms;

namespace TfsMerge.Controller
{
    public class HMergeController
    {
        public BindingList<HMergeItem> HMergeItems { get; private set; }

        public HMergeController(int changesetId, MainController controller)
        {
            var list = new List<HMergeItem>();

            var branches = controller.Tfs.GetBranchHierarchy(changesetId);
            if (branches == null)
                return;

            var mergedBranches = GetMergedBranches(controller.Tfs.TrackMerges(changesetId).ToArray()).ToArray();

            foreach (var branch in branches)
            {
                if (branch.Properties.RootItem == null)
                    continue;

                var parentBranch = string.Empty;
                if (branch.Properties.ParentBranch != null)
                    parentBranch = branch.Properties.ParentBranch.Item;

                var itm = new HMergeItem(branch.Properties.RootItem.Item, parentBranch);

                var mergedBranch = mergedBranches.FirstOrDefault(x => x.Item == branch.Properties.RootItem.Item);
                if (mergedBranch != null)
                    itm.SetChangesetInfo(mergedBranch.Changeset.ChangesetId, mergedBranch.Changeset.Committer, mergedBranch.Changeset.CreationDate);

                list.Add(itm);
            }

            if (!mergedBranches.Any())
            {
                var changeset = controller.Tfs.GetChangeset(changesetId);
                var branch = controller.Tfs.GetBranch(changesetId);

                var itm = list.FirstOrDefault(x => x.ID == branch);
                if (itm != null)
                    itm.SetChangesetInfo(changeset.ChangesetId, changeset.Committer, changeset.CreationDate);
            }

            SetPrimary(list, changesetId);

            HMergeItems = new BindingList<HMergeItem>(list.OrderBy(x => x.ID).ToList());
        }

        private static void SetPrimary(IEnumerable<HMergeItem> list, int changesetId)
        {
            var primary = list.FirstOrDefault(x => x.ChangesetId == changesetId);
            if (primary != null)
                primary.SetPrimary();
        }

        private static IEnumerable<dynamic> GetMergedBranches(ExtendedMerge[] merges)
        {
            foreach (var itm in merges)
            {
                if (merges.FirstOrDefault(x => x.SourceItem.Item.ServerItem == itm.TargetItem.Item) == null)
                {
                    yield return new { Item = itm.SourceItem.Item.ServerItem, Changeset = itm.SourceChangeset };
                    yield return new { Item = itm.TargetItem.Item, Changeset = itm.TargetChangeset };
                }
                else
                    yield return new { Item = itm.SourceItem.Item.ServerItem, Changeset = itm.SourceChangeset };
            }
        }

        public IEnumerable<HMergeItem> GetPathToPrimary(HMergeItem itm)
        {
            var primary = HMergeItems.FirstOrDefault(x => x.IsPrimary);
            if (primary == null)
                return new List<HMergeItem>();

            var primaryChain = GetPrimaryChain().ToArray();
            var itmChain = GetHMergeItemChain(itm).ToArray();

            if (primaryChain.Contains(itm))
                return primaryChain
                    .TakeWhile(x => x.ID != itm.ID)
                    .Concat(new[] { itm });

            if (itmChain.Contains(primary))
                return itmChain.SkipWhile(x => x.ID != primary.ID);

            return primaryChain.Concat(itmChain.Skip(1));
        }

        private IEnumerable<HMergeItem> GetPrimaryChain()
        {
            var itm = HMergeItems.FirstOrDefault(x => x.IsPrimary);

            while (itm != null)
            {
                yield return itm;
                itm = HMergeItems.FirstOrDefault(x => x.ID == itm.ParentID);
            }
        }

        private IEnumerable<HMergeItem> GetHMergeItemChain(HMergeItem itm)
        {
            var list = new List<HMergeItem>();

            while (itm != null)
            {
                list.Add(itm);
                itm = HMergeItems.FirstOrDefault(x => x.ID == itm.ParentID);
            }

            return list.Reverse<HMergeItem>();
        }

        public bool Validate(Form owner, IEnumerable<HMergeItem> pathToPrimary)
        {
            if (pathToPrimary.FirstOrDefault(x => !x.ChangesetId.HasValue) == null)
            {
                MessageBox.Show(owner, "No changeset for merge...", "Information...");
                return false;
            }

            return true;
        }
    }
}
